module MIPS_top(
    input clk, rst
);

// IF output signals wire
wire [31:0] Pcout4_f_IF;
wire [31:0] inst_f_IF;

// register IF/ID signals
wire [31:0] pc4_f_IFID, inst_f_IFID;

//**************
// ID output 
// hazard module
wire IFIDWait_f_hd;
wire PCwait_f_hd;
wire Detect_to_Control_f_hd;

// control signals
wire JRFlush_f_id, JFlush_f_id;            // Jump signals,work at ID
wire branch_beq_f_id, branch_bne_f_id;     // work at MEM
wire JALFlush_f_id;                   // work at WB
wire ALUSrc_f_id, RegDst_f_id;             // work at EX 
wire [2:0] ALUop_f_id;                // work at EX
wire SignSelect_f_id;
wire MemWrite_f_id, MeMRead_f_id;          // work at MEM   
wire RegWrite_f_id, MeMtoReg_f_id; 

// from regs
wire [31:0] ReadData_rs_f_reg, ReadData_rt_f_reg;

// Addr of J instruction
wire [31:0] JumpAddr_f_JAddr;

// extent of immediate 
wire [31:0] SignImme_f_Sign;
wire [31:0] ZeroExtend_f_sign;

// signals from ID/EX
wire beq_f_id_ex, bne_f_id_ex;     // work at MEM
wire JALFlush_f_id_ex;                        // work at WB
wire ALUSrc_f_id_ex, RegDst_f_id_ex;               // work at EX 
wire SignSelect_f_id_ex;
wire [2:0] ALUop_f_id_ex;                       // work at EX
wire MemWrite_f_id_ex, MeMRead_f_id_ex;            // work at MEM   
wire RegWrite_f_id_ex, MeMtoReg_f_id_ex;           // work at WB
wire [31:0] pc4_f_id_ex;                        // pc+4
wire [31:0] inst_f_id_ex;                       // instruction
wire [31:0] Ext_imm_f_id_ex;                    //sign extend value
wire [31:0] Ext_zero_f_id_ex;
wire [31:0] Data_rs_f_id_ex, Data_rt_f_id_ex;             // data from regs 
wire [31:0] JalAddr_f_id_ex;

//**********************
// EX part
wire [31:0] pc8_f_ex;
wire [31:0] AddrBranch_f_ex;

// alu 
wire [31:0] alu1, alu2_1, alu2_2 ,alu2_sign_mux;
wire [1:0] ForwardA_f_forward, ForwardB_f_forward;

wire [31:0] rd_f_alu;  //more generally, it is just ALUout
wire Zero_f_alu; 
wire overflow_f_alu;
wire [4:0] rd_f_mux;

// ex/mem
wire beq_f_ex_mem, bne_f_ex_mem;                  // work at MEM
wire JALFlush_f_ex_mem;                          // work at WB
wire MemWrite_f_ex_mem, MeMRead_f_ex_mem;           // work at MEM   
wire RegWrite_f_ex_mem, MeMtoReg_f_ex_mem;          // work at WB
wire [31:0] JalAddr_f_ex_mem;                 // work at WB    
wire [31:0] pc8_f_ex_mem;                     // work at WB    
wire [4:0]  rd_f_ex_mem;                      // work at WB
wire  overflow_f_ex_mem;                      // work at WB
wire  zero_f_ex_mem;                          // work at MEM
wire [31:0] BranchAddr_f_ex_mem;              // address of bne,beq,work at MEM
wire [31:0] Res_f_ex_mem;                     // work at MEM
wire [31:0] Data_rt_f_ex_mem;

//****************
// mem

wire Pcscr_f_mem;
wire [31:0] rdata_f_dcache;

// mem_wb
wire JALFlush_f_mem_wb;                            // work at WB  
wire RegWrite_f_mem_wb, MeMtoReg_f_mem_wb;          // work at WB
wire [31:0] JalAddr_f_mem_wb;                 // work at WB    
wire [31:0] pc8_f_mem_wb;                     // work at WB    
wire [4:0]  rd_f_mem_wb;                      // work at WB
wire [31:0] Res_f_mem_wb;                     // work at WB
wire  overflow_f_mem_wb;                      // work at WB
wire [31:0] Data_f_mem_wb;

//***************
// wb
wire Regwrite_f_wb;
wire JALFlush_f_wb;
wire [31:0] JalAddr_f_wb;
wire [31:0] wb_data_f_wb;
wire [4:0] wb_addr_f_wb;

IF uut_if(
    .clk(clk), .rst(rst),
    .jr_from_id(JRFlush_f_id), 
    .j_from_id(JFlush_f_id), 
    .Pcscr_from_mem(Pcscr_f_mem), 
    .jal_from_wb(JALFlush_f_wb),
    .JrAddr_f_id(ReadData_rs_f_reg), 
    .JAddr_f_id(JumpAddr_f_JAddr), 
    .JalAddr_f_wb(JalAddr_f_wb), 
    .PscrAddr_f_mem(BranchAddr_f_ex_mem),
    .PCwait(PCwait_f_hd),
    .inst(inst_f_IF),
    .PCout4(Pcout4_f_IF)    
);

IFID uut_ifid(
    .clk(clk), .rst(rst), 
    .IFIDFlush(1'b0),                           // IFIDFlush clear signals
    .IFIDWait(IFIDWait_f_hd),                   // IFIDWait keep signals
    .JRFlush_from_ID(JRFlush_f_id),
    .JFlush_from_ID(JFlush_f_id),
    .JALFlush_from_WB(JALFlush_f_wb),
    .PCScr(Pcscr_f_mem),
    .inst_from_IF(inst_f_IF),                           // current instruction
    .pc4_form_IF(Pcout4_f_IF),                             // next pc value
    .pc4_to_ID(pc4_f_IFID), 
    .inst_to_ID(inst_f_IFID)
);
HazardDetext uut_hd(
    .MeMRead_from_ID_EX(MeMRead_f_id_ex),
    .rt_from_ID_EX(inst_f_id_ex[20:16]),
    .rs_from_IF_ID(inst_f_IFID[25:21]), 
    .rt_from_IF_ID(inst_f_IFID[20:16]),
    .IFIDWait(IFIDWait_f_hd), 
    .PCwait(PCwait_f_hd), 
    .Detect_to_Control(Detect_to_Control_f_hd)
);
MIPSControl uut_control(
    .inst_from_IF_ID(inst_f_IFID),
    .clk(clk), .rst(rst),
    .Detect_to_Control(Detect_to_Control_f_hd),               // clear signals when it is true
    
    .JRFlush(JRFlush_f_id), 
    .JFlush(JFlush_f_id),            // Jump signals,work at ID
    .branch_beq(branch_beq_f_id), 
    .branch_bne(branch_bne_f_id),     // work at MEM
    .JALFlush(JALFlush_f_id),                   // work at WB

    .ALUSrc(ALUSrc_f_id), 
    .RegDst(RegDst_f_id),             // work at EX 
    .ALUop(ALUop_f_id),                // work at EX
    .MemWrite(MemWrite_f_id), 
    .MeMRead(MeMRead_f_id),          // work at MEM   
    .RegWrite(RegWrite_f_id), 
    .MeMtoReg(MeMtoReg_f_id),          // work at WB
    .SignSelect(SignSelect_f_id)
);
Regs uut_regs(
    .clk(clk), .rst(rst), 
    .RegWrite(Regwrite_f_wb),
    .ReadAddr_rs(inst_f_IFID[25:21]), 
    .ReadAddr_rt(inst_f_IFID[20:16]),
    .WriteAddr_rd(wb_addr_f_wb),              //需要注意的是不同指令可能名称不同
    .WriteData(wb_data_f_wb),
    .ReadData_rs(ReadData_rs_f_reg), 
    .ReadData_rt(ReadData_rt_f_reg) 
);
IdJumpAddr uut_idjumpAddr(
    .instr_index_of_inst(inst_f_IFID[25:0]),
    .Pc4_from_IF_ID(pc4_f_IFID),
    .JumpAddr_to_IF(JumpAddr_f_JAddr)
);
SignExtend uut_sign(
    .imme_of_inst(inst_f_IFID[15:0]),
    .SignImme(SignImme_f_Sign),
    .ZeroExtend(ZeroExtend_f_sign)
);
IDEX uut_idex(
    .clk(clk), .rst(rst), 
    .JALFlush_from_WB(JALFlush_f_wb),   // flush IDEX regiater
    .PCScr_from_MEM(Pcscr_f_mem),

    // control signals
    .branch_beq_from_ID(branch_beq_f_id), 
    .branch_bne_from_ID(branch_bne_f_id),      // work at MEM
    .JALFlush_from_ID(JALFlush_f_id),                            // work at WB

    .ALUSrc_from_ID(ALUSrc_f_id), 
    .RegDst_from_ID(RegDst_f_id),              // work at EX 
    .ALUop_from_ID(ALUop_f_id),                         // work at EX
    .SignSelect_from_ID(SignSelect_f_id),                    //////////////////////
    .MemWrite_from_ID(MemWrite_f_id), 
    .MeMRead_from_ID(MeMRead_f_id),           // work at MEM   
    .RegWrite_from_ID(RegWrite_f_id), 
    .MeMtoReg_from_ID(MeMtoReg_f_id),          // work at WB
    // data
    .pc4_from_ID(pc4_f_IFID),                  // pc+4
    .inst_from_ID(inst_f_IFID),                 // instruction
    .Ext_imm_from_ID(SignImme_f_Sign),              //sign extend value
    .Ext_zero_from_ID(ZeroExtend_f_sign),
    .Data_from_rs(ReadData_rs_f_reg), 
    .Data_from_rt(ReadData_rt_f_reg),   // data from regs 
    .JalAddr_from_ID(JumpAddr_f_JAddr),                 //work at WB

    // output
    .branch_beq_2_EX(beq_f_id_ex), 
    .branch_bne_2_EX(bne_f_id_ex),     // work at MEM
    .JALFlush_2_EX(JALFlush_f_id_ex),                        // work at WB

    .ALUSrc_2_EX(ALUSrc_f_id_ex), 
    .RegDst_2_EX(RegDst_f_id_ex),               // work at EX 
    .ALUop_2_EX(ALUop_f_id_ex),                       // work at EX
    .SignSelect_2_EX(SignSelect_f_id_ex),
    .MemWrite_2_EX(MemWrite_f_id_ex), 
    .MeMRead_2_EX(MeMRead_f_id_ex),            // work at MEM   
    .RegWrite_2_EX(RegWrite_f_id_ex), 
    .MeMtoReg_2_EX(MeMtoReg_f_id_ex),           // work at WB

    .pc4_2_EX(pc4_f_id_ex),                        // pc+4
    .inst_2_EX(inst_f_id_ex),                       // instruction
    .Ext_imm_2_EX(Ext_imm_f_id_ex),                    //sign extend value
    .Ext_zero_2_EX(Ext_zero_f_id_ex),
    .Data_2_rs(Data_rs_f_id_ex), 
    .Data_2_rt(Data_rt_f_id_ex),             // data from regs 
    .JalAddr_2_EX(JalAddr_f_id_ex)
);

//******************
// EX part
// bne, beq address generate and back up pc8 for jal
AddrGenerate uut_jal_branch(
    .pc4_from_ID_EX(pc4_f_id_ex),
    .Ext_imm_from_ID_EX(Ext_imm_f_id_ex),
    .pc8_EX(pc8_f_ex),
    .AddrBranch(AddrBranch_f_ex)            //Address of bne,beq
);
// ALU 
// mux at input 1 of alu
mux3 uut_mux_in1_alu(
    .in0(Data_rs_f_id_ex), 
    .in1(wb_data_f_wb), 
    .in2(Res_f_ex_mem),
    .choose(ForwardA_f_forward),
    .lucky(alu1)
);
mux3 uut_mux_in2_alu(
    .in0(Data_rt_f_id_ex), 
    .in1(wb_data_f_wb), 
    .in2(Res_f_ex_mem),
    .choose(ForwardB_f_forward),
    .lucky(alu2_1)
);
mux2 uut_mux2_alu(
    .in0(alu2_1), 
    .in1(alu2_sign_mux),
    .choose(ALUSrc_f_id_ex),
    .lucky(alu2_2)
);
mux2 uut_mux_sign(           // rt, decide the signed_extend or zero_extend
    .in0(Ext_zero_f_id_ex), 
    .in1(Ext_imm_f_id_ex),
    .choose(SignSelect_f_id_ex),
    .lucky(alu2_sign_mux)
);
ALU uut_alu(
    .rs(alu1),  //也作为访存指令中的base
    .rt(alu2_2),
    .sa(inst_f_id_ex[10:6]),   // ins[10:6] of R type    
    .ALUop(ALUop_f_id_ex),
    .FuncCode(inst_f_id_ex[5:0]),
    .rd(rd_f_alu),  //more generally, it is just ALUout
    .Zero(Zero_f_alu), 
    .overflow(overflow_f_alu)
);
mux2_5 uut_wbAddrSelect(
    .in0(inst_f_id_ex[20:16]),    // rt
    .in1(inst_f_id_ex[15:11]),    // rd
    .choose(RegDst_f_id_ex),
    .lucky(rd_f_mux)
);
Forward uut_forward(
    .rt_from_ID_EX(inst_f_id_ex[20:16]), 
    .rs_from_ID_EX(inst_f_id_ex[25:21]),
    .rd_from_EX_MEM(rd_f_ex_mem), 
    .rd_from_MEM_WB(rd_f_mem_wb),
    .EX_MEM_RegWrite(RegWrite_f_ex_mem), 
    .MEM_WB_RegWrite(RegWrite_f_mem_wb),
    .ForwardA(ForwardA_f_forward), 
    .ForwardB(ForwardB_f_forward)
);
EXMEM uut_ex_mem(
    .clk(clk),.rst(rst),
    .JALFlush_from_WB(JALFlush_f_wb),   // flush IDEX regiater
    .PCScr_from_MEM(Pcscr_f_mem),

    // control signals
    .branch_beq_from_EX(beq_f_id_ex), 
    .branch_bne_from_EX(bne_f_id_ex),      // work at MEM
    .JALFlush_from_EX(JALFlush_f_id_ex),                            // work at WB

    .MemWrite_from_EX(MemWrite_f_id_ex), 
    .MeMRead_from_EX(MeMRead_f_id_ex),           // work at MEM   
    .RegWrite_from_EX(RegWrite_f_id_ex), 
    .MeMtoReg_from_EX(MeMtoReg_f_id_ex),          // work at WB
    
    // data
    .JalAddr_from_EX(JalAddr_f_id_ex),                 // work at WB    
    .pc8_from_Ex(pc8_f_ex),                     // work at WB    
    .rd_from_mux(rd_f_mux),                     // work at WB
    .overflow_from_ALU(overflow_f_alu),                     // work at WB
    .zero_from_ALU(Zero_f_alu),                         // work at MEM
    .BranchAddr_from_EX(AddrBranch_f_ex),              // address of bne,beq,work at MEM
    .Res_from_ALU(rd_f_alu),                    // work at MEM
    .Data_rt_from_EX(alu2_1),                 // work at MEM, to Dcache
    
    // out     
    .beq_2_MEM(beq_f_ex_mem), 
    .bne_2_MEM(bne_f_ex_mem),                  // work at MEM
    .JALFlush_2_MEM(JALFlush_f_ex_mem),
    .MemWrite_2_MEM(MemWrite_f_ex_mem), 
    .MeMRead_2_MEM(MeMRead_f_ex_mem),           // work at MEM   
    .RegWrite_2_MEM(RegWrite_f_ex_mem), 
    .MeMtoReg_2_MEM(MeMtoReg_f_ex_mem),          // work at WB
    .JalAddr_2_MEM(JalAddr_f_ex_mem),                 // work at WB    
    .pc8_2_MEM(pc8_f_ex_mem),                     // work at WB    
    .rd_2_MEM(rd_f_ex_mem),                      // work at WB
    .overflow_2_MEM(overflow_f_ex_mem),                      // work at WB
    .zero_2_MEM(zero_f_ex_mem),                          // work at MEM
    .BranchAddr_2_MEM(BranchAddr_f_ex_mem),              // address of bne,beq,work at MEM
    .Res_2_MEM(Res_f_ex_mem),                     // work at MEM
    .Data_rt_2_MEM(Data_rt_f_ex_mem)                  // work at MEM, to Dcache
);

//*************
// MEM
PCscr uut_pcscr(
    .bne_EX_MEM(bne_f_ex_mem), 
    .beq_EX_MEM(beq_f_ex_mem),
    .zero_EX_MEM(zero_f_ex_mem),
    .Pcscr(Pcscr_f_mem)
);
dcache uut_dcache(
    .clk(clk),.reset(rst),
    .we(MemWrite_f_ex_mem),
    .re(MeMRead_f_ex_mem),
    .addr(Res_f_ex_mem),
    .wdata(Data_rt_f_ex_mem),
    .rdata(rdata_f_dcache)
);
MEMWB uut_mem_wb(
    .clk(clk), .rst(rst),
    .JALFlush_from_WB(JALFlush_f_wb),   // flush IDEX regiater
    // control signals    
    .JALFlush_from_MEM(JALFlush_f_ex_mem),                            // work at WB  
    .RegWrite_from_MEM(RegWrite_f_ex_mem), 
    .MeMtoReg_from_MEM(MeMtoReg_f_ex_mem),          // work at WB
    
    // data
    .JalAddr_from_MEM(JalAddr_f_ex_mem),                 // work at WB    
    .pc8_from_MEM(pc8_f_ex_mem),                     // work at WB    
    .rd_from_MEM(rd_f_ex_mem),                      // work at WB
    .Res_from_MEM(Res_f_ex_mem),                     // work at WB
    .overflow_from_MEM(overflow_f_ex_mem),                      // work at WB
    .Data_from_dcache(rdata_f_dcache),                 // work at WB

    // out
    .JALFlush_2_WB(JALFlush_f_mem_wb),                            // work at WB  
    .RegWrite_2_WB(RegWrite_f_mem_wb), 
    .MeMtoReg_2_WB(MeMtoReg_f_mem_wb),          // work at WB
    
    // data
    .JalAddr_2_WB(JalAddr_f_mem_wb),                 // work at WB    
    .pc8_2_WB(pc8_f_mem_wb),                     // work at WB    
    .rd_2_WB(rd_f_mem_wb),                      // work at WB
    .Res_2_WB(Res_f_mem_wb),                     // work at WB
    .overflow_2_WB(overflow_f_mem_wb),                      // work at WB
    .Data_2_WB(Data_f_mem_wb)                  // work at WB    
);
WB uut_wb(
    // control signals    
    .JALFlush_from_MEM_WB(JALFlush_f_mem_wb),                            // work at WB  
    .RegWrite_from_MEM_WB(RegWrite_f_mem_wb), 
    .MeMtoReg_from_MEM_WB(MeMtoReg_f_mem_wb),          // work at WB
    
    // data
    .JalAddr_from_MEM_WB(JalAddr_f_mem_wb),                 // work at WB    
    .pc8_from_MEM_WB(pc8_f_mem_wb),                     // work at WB    
    .rd_from_MEM_WB(rd_f_mem_wb),                      // work at WB
    .Res_from_MEM_WB(Res_f_mem_wb),                     // work at WB
    .overflow_from_MEM_WB(overflow_f_mem_wb),                      // work at WB
    .Data_from_MEM_WB(Data_f_mem_wb),                 // work at WB

    .Regwrite_2_regs(Regwrite_f_wb),
    .JALFlush_2_IF(JALFlush_f_wb),
    .JalAddr_2_IF(JalAddr_f_wb),
    .wb_data(wb_data_f_wb),
    .wb_addr(wb_addr_f_wb)
);

endmodule

//
// Instruction Fetch
//
module IF(
    input clk, rst,
    input jr_from_id, j_from_id, Pcscr_from_mem, jal_from_wb,
    input [31:0] JrAddr_f_id, JAddr_f_id, JalAddr_f_wb, PscrAddr_f_mem,
    input PCwait,
    output [31:0] inst,
    output [31:0] PCout4    
);
// wire form if
wire [31:0] pc_mux_reg;
wire [31:0] pc4_reg_mux;
wire [31:0] PCout;

assign PCout4 = pc4_reg_mux;
pcmux uut_pcmux(
    .jr(jr_from_id), .j(j_from_id), .Pcscr(Pcscr_from_mem), .jal(jal_from_wb),
    .pc4(pc4_reg_mux), .JrAddr(JrAddr_f_id), .JAddr(JAddr_f_id), .JalAddr(JalAddr_f_wb),
    .PscrAddr(PscrAddr_f_mem), .CurrentPC(pc_mux_reg)
);
PCreg uut_PCreg(
    .clk(clk), .rst(rst),
    .PCwait(PCwait),       //keep the PC value when pipeline stall
    .PCin(pc_mux_reg),
    .PCout(PCout),
    .PCout4(pc4_reg_mux)
);
icache uut_ica(
	.rst(rst),
	.pc(PCout),
	.inst(inst)
);
endmodule

module pcmux(
    input jr, j, Pcscr, jal,
    input [31:0] pc4, JrAddr, JAddr, JalAddr, PscrAddr,
    output [31:0] CurrentPC
);
wire [31:0] w1, w2, w3;

assign w1 = (jr)? JrAddr:pc4;
assign w2 = (j)?  JAddr:w1;
assign w3 = (Pcscr)? PscrAddr:w2;
assign CurrentPC = (jal)? JalAddr:w3;
endmodule

module mux3(
    input [31:0] in0, in1, in2,
    input [1:0] choose,
    output reg[31:0] lucky
);
always@(*) begin
    case(choose)
         2'b00,2'b11: lucky <= in0;
         2'b01: lucky <= in1;
         2'b10: lucky <= in2;
    endcase
end
endmodule

module mux2(
    input [31:0] in0, in1,
    input choose,
    output [31:0] lucky
);
assign lucky = (choose)? in1:in0;
endmodule

module mux2_5(
    input [4:0] in0, in1,
    input choose,
    output [4:0] lucky
);
assign lucky = (choose)? in1:in0;
endmodule